#include <windows.h>//include Windows.h before tchar.h & strsafe.h
#include <tchar.h>
#include <strsafe.h>

#include <iostream>
#include <sstream>
#include <iomanip>

#include "pthread.h"
#include "mqueue.h"

#include "w32cmn.h"
#include "iflexr.h"
#include "dte.h"



class CDteConsole
	: public Serialflexer<DTE_READ|DTE_WRITE|DTE_STATUS>
{
public:
	CDteConsole();
	~CDteConsole();
	HANDLE h;

protected:
	// SCB
	virtual DWORD SCBDataRecv(LPBYTE buffer, DWORD size);

	virtual void SCBComStat(BOOL fCtsHold, BOOL fDsrHold, 
		BOOL fRlsdHold, BOOL fXoffHold, BOOL fXoffSent, BOOL fEof, BOOL fTxim,
		BOOL cbOutQue, BOOL cbInQue);
	virtual void SCBStatusEvent(BOOL fCTS, BOOL fDSR, BOOL fERR, 
		BOOL fRING, BOOL fRLSD, BOOL fBREAK, BOOL fRXCHAR, 
		BOOL fRXFLAG, BOOL fTXEMPTY);
	virtual void SCBModemStatus(BOOL fCTS, BOOL fDSR, BOOL fRING, BOOL fRLSD);
	virtual void SCBCommError(BOOL fDNS, BOOL fIOE, BOOL fOOP, 
		BOOL fPTO, BOOL fMODE, BOOL fBREAK, BOOL fFRAME, BOOL fRXOVER, 
		BOOL fTXFULL, BOOL fOVERRUN, BOOL fRXPARITY);

	virtual void SCBReadAborted(void);
	virtual void SCBWaitCommEventAborted(void);

	virtual void SCBErrorReporter( DWORD dwErrorCode/*TCHAR * szMessage*/ );
	virtual void SCBErrorInComm(void);
	virtual void SCBReadTimeoutOverlapped(void);
	virtual void SCBReatTimeoutImmediately(void);
};
///////////////////////////////////////////////////////////////////////////////
void CDteConsole::SCBReatTimeoutImmediately(void)
{
	std::cout<<_T("Read timed out immediately.\r\n");
}

void CDteConsole::SCBReadTimeoutOverlapped(void) 
{
	std::cout<<_T("Read timed out overlapped.\r\n");
}

void CDteConsole::SCBReadAborted(void)
{
	std::cout<<"Read aborted"<<std::endl;
}

void CDteConsole::SCBWaitCommEventAborted(void)
{
	std::cout<<"status WaitCommEvent aborted"<<std::endl;
}


void CDteConsole::SCBCommError(BOOL fDNS, BOOL fIOE, BOOL fOOP, 
		BOOL fPTO, BOOL fMODE, BOOL fBREAK, BOOL fFRAME, BOOL fRXOVER, 
		BOOL fTXFULL, BOOL fOVERRUN, BOOL fRXPARITY)
{
	std::cout<<"SCBCommError"<<","<<fDNS<<","<<fIOE<<","<<fOOP<<","<<fPTO<<","<<fMODE<<","<<fBREAK<<","<<fFRAME<<","<<fRXOVER<<","<<fTXFULL<<","<<fOVERRUN<<","<<fRXPARITY<<std::endl;
}

void CDteConsole::SCBModemStatus(BOOL fCTS, BOOL fDSR, BOOL fRING, BOOL fRLSD)
{
	std::cout<<"SCBModemStatus"<<","<<fCTS<<","<<fDSR<<","<<fRING<<","<<fRLSD<<std::endl;
	if(fRLSD)
		std::cout<<"Carrier Detected."<<std::endl;
}

void CDteConsole::SCBStatusEvent(BOOL fCTS, BOOL fDSR, BOOL fERR, 
		BOOL fRING, BOOL fRLSD, BOOL fBREAK, BOOL fRXCHAR, 
		BOOL fRXFLAG, BOOL fTXEMPTY)
{
	std::cout<<"SCBStatusEvent"<<","<<fCTS<<","<<fDSR<<","<<fERR<<","<<fRING<<","<<fRLSD<<","<<fBREAK<<","<<fRXCHAR<<","<<fRXFLAG<<","<<fTXEMPTY<<std::endl;
	if(fRING)
		std::cout<<"A ring indicator was detected."<<std::endl;
}

void CDteConsole::SCBErrorInComm(void)
{
	// quit program?
}

void CDteConsole::SCBComStat(BOOL fCtsHold, BOOL fDsrHold, 
		BOOL fRlsdHold, BOOL fXoffHold, BOOL fXoffSent, BOOL fEof, BOOL fTxim,
		BOOL cbOutQue, BOOL cbInQue)
{
	std::cout<<"SCBComStat"<<","<<fCtsHold<<","<<fDsrHold<<","<<fRlsdHold<<","<<fXoffHold<<","<<fXoffSent<<","<<fEof<<","<<fTxim<<","<<cbOutQue<<","<<cbInQue<<std::endl;
	TCHAR szMessage[70]=_T("");
    //
    // Show COMSTAT structure with the error indicator
    //
    if (fCtsHold)
        std::cout<<("Tx waiting for CTS signal.\r\n");

    if (fDsrHold)
        std::cout<<("Tx waiting for DSR signal.\r\n");

    if (fRlsdHold)
        std::cout<<("Tx waiting for RLSD signal.\r\n");

    if (fXoffHold)
        std::cout<<("Tx waiting, XOFF char rec'd.\r\n");

    if (fXoffSent)
        std::cout<<("Tx waiting, XOFF char sent.\r\n");
    
    if (fEof)
        std::cout<<("EOF character received.\r\n");
    
    if (fTxim)
        std::cout<<("Character waiting for Tx.\r\n");

    if (cbInQue) {
        wsprintf(szMessage, _T("%d bytes in input buffer.\r\n"), cbInQue);
        std::cout<<(szMessage);
    }

    if (cbOutQue) {
        wsprintf(szMessage, _T("%d bytes in output buffer.\r\n"), cbOutQue);
        std::cout<<(szMessage);
    }
}

CDteConsole::CDteConsole()
{
}

CDteConsole::~CDteConsole()
{
}

//
//std::ios::hex is a bitmask, 8 on gcc and 2048 on VC. That's why it
//works with setf().
//
DWORD CDteConsole::SCBDataRecv(LPBYTE buffer, DWORD size)
{
	std::cout<<std::dec<<"Data Received, size: "<<size<<std::endl;
	for(DWORD i=0; i<size; i++)
	{
		std::cout<<std::hex<<std::setw(2)<<std::setfill('0')<<(int)(buffer[i])<<" ";
	}
	std::cout<<std::endl;
	//std::cout<<std::string((CHAR*)buffer, size)<<std::endl;
	return 0;
}

void CDteConsole::SCBErrorReporter( DWORD dwErrorCode/*TCHAR * szMessage*/ )
{
	std::cout<<"SCB ERROR CODE "<<dwErrorCode<<std::endl;
}

///////////////////////////////////////////////////////////////////////////////

int main(int argc, char** argv)
{
	pthread_win32_process_attach_np();

	CDteConsole dte;
	dte.Open(0, _T("baud=9600 parity=N data=8 stop=1") );
	

	std::string input;
	//std::stringstream inputstream(std::cin);
	while(1)
	{
		std::cout<<"Press 'q' to quit:";
		std::cin>>input;
		if(input=="q" || input=="Q")
			break;

		input+="\x0d";
		dte.Write((LPBYTE)input.c_str(), input.length());
	};

	dte.Close();
	pthread_win32_process_detach_np();
}
